#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
串口通信处理模块
Multi-threaded Serial Communication Handler

支持特性：
- 多线程串口通信
- 9位接收模式支持
- 数据队列管理
- 自动重连机制
- 命令超时处理
"""

import serial
import serial.tools.list_ports
import threading
import queue
import time
import logging
from typing import Optional, List, Callable, Any
from dataclasses import dataclass
from enum import Enum

from serial_protocol import NSA5312Protocol, CommandCode, StatusCode

class ConnectionState(Enum):
    """连接状态枚举"""
    DISCONNECTED = "断开连接"
    CONNECTING = "正在连接"
    CONNECTED = "已连接"
    ERROR = "连接错误"

@dataclass
class SerialConfig:
    """串口配置"""
    port: str
    baudrate: int = 9600
    bytesize: int = serial.EIGHTBITS
    parity: str = serial.PARITY_NONE
    stopbits: int = serial.STOPBITS_ONE
    timeout: float = 1.0
    write_timeout: float = 1.0

@dataclass
class CommandResponse:
    """命令响应"""
    success: bool
    command: int
    status: int
    data: bytes
    error_msg: str = ""
    timestamp: float = 0.0

class SerialHandler:
    """串口通信处理器"""
    
    def __init__(self, log_callback: Optional[Callable[[str], None]] = None):
        self.serial_port: Optional[serial.Serial] = None
        self.config: Optional[SerialConfig] = None
        self.protocol = NSA5312Protocol()
        
        # 线程控制
        self.is_running = False
        self.read_thread: Optional[threading.Thread] = None
        self.write_thread: Optional[threading.Thread] = None
        
        # 数据队列
        self.write_queue = queue.Queue()
        self.response_queue = queue.Queue()
        self.pending_commands = {}  # 等待响应的命令
        
        # 状态管理
        self.connection_state = ConnectionState.DISCONNECTED
        self.state_lock = threading.Lock()
        self.last_error = ""
        
        # 回调函数
        self.log_callback = log_callback
        self.data_callback: Optional[Callable[[bytes], None]] = None
        self.state_callback: Optional[Callable[[ConnectionState], None]] = None
        
        # 配置日志
        self.logger = logging.getLogger(__name__)
        if not self.logger.handlers:
            handler = logging.StreamHandler()
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
            handler.setFormatter(formatter)
            self.logger.addHandler(handler)
            self.logger.setLevel(logging.INFO)
    
    def log(self, message: str, level: str = "INFO"):
        """记录日志"""
        if self.log_callback:
            self.log_callback(f"[{level}] {message}")
        
        if level == "ERROR":
            self.logger.error(message)
        elif level == "WARNING":
            self.logger.warning(message)
        elif level == "DEBUG":
            self.logger.debug(message)
        else:
            self.logger.info(message)
    
    def get_available_ports(self) -> List[str]:
        """获取可用串口列表"""
        ports = []
        for port in serial.tools.list_ports.comports():
            ports.append(port.device)
        return ports
    
    def set_config(self, config: SerialConfig):
        """设置串口配置"""
        self.config = config
        self.log(f"串口配置更新: {config.port} @ {config.baudrate}")
    
    def set_callbacks(self, data_callback: Optional[Callable[[bytes], None]] = None,
                     state_callback: Optional[Callable[[ConnectionState], None]] = None):
        """设置回调函数"""
        self.data_callback = data_callback
        self.state_callback = state_callback
    
    def _update_state(self, new_state: ConnectionState, error_msg: str = ""):
        """更新连接状态"""
        with self.state_lock:
            if self.connection_state != new_state:
                self.connection_state = new_state
                self.last_error = error_msg
                
                if self.state_callback:
                    self.state_callback(new_state)
                
                self.log(f"连接状态: {new_state.value}")
                if error_msg:
                    self.log(f"错误信息: {error_msg}", "ERROR")
    
    def connect(self) -> bool:
        """连接串口"""
        if not self.config:
            self._update_state(ConnectionState.ERROR, "串口配置未设置")
            return False
        
        if self.is_running:
            self.log("串口已连接", "WARNING")
            return True
        
        try:
            self._update_state(ConnectionState.CONNECTING)
            
            # 创建串口对象
            self.serial_port = serial.Serial(
                port=self.config.port,
                baudrate=self.config.baudrate,
                bytesize=self.config.bytesize,
                parity=serial.PARITY_SPACE,  # 默认使用Space校验位（数据传输）
                stopbits=self.config.stopbits,
                timeout=self.config.timeout,
                write_timeout=self.config.write_timeout
            )
            
            # 配置9位数据接收（如果支持）
            # 注意：标准pyserial不直接支持9位数据，需要特殊处理
            # 这里我们使用8位模式，依赖MCU的协议设计
            
            # 启动线程
            self.is_running = True
            self.read_thread = threading.Thread(target=self._read_worker, daemon=True)
            self.write_thread = threading.Thread(target=self._write_worker, daemon=True)
            
            self.read_thread.start()
            self.write_thread.start()
            
            self._update_state(ConnectionState.CONNECTED)
            self.log(f"串口连接成功: {self.config.port}")
            return True
            
        except Exception as e:
            self._update_state(ConnectionState.ERROR, str(e))
            self.log(f"串口连接失败: {e}", "ERROR")
            return False
    
    def disconnect(self):
        """断开串口连接"""
        self.is_running = False
        
        # 等待线程结束
        if self.read_thread and self.read_thread.is_alive():
            self.read_thread.join(timeout=2.0)
        
        if self.write_thread and self.write_thread.is_alive():
            self.write_thread.join(timeout=2.0)
        
        # 关闭串口
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.close()
            self.serial_port = None
        
        # 清空队列
        self._clear_queues()
        
        self._update_state(ConnectionState.DISCONNECTED)
        self.log("串口连接已断开")
    
    def _clear_queues(self):
        """清空队列"""
        while not self.write_queue.empty():
            try:
                self.write_queue.get_nowait()
            except queue.Empty:
                break
        
        while not self.response_queue.empty():
            try:
                self.response_queue.get_nowait()
            except queue.Empty:
                break
        
        self.pending_commands.clear()
    
    def _read_worker(self):
        """读取线程工作函数"""
        buffer = b''
        
        while self.is_running and self.serial_port and self.serial_port.is_open:
            try:
                # 读取数据
                data = self.serial_port.read(1)
                if not data:
                    continue
                
                buffer += data
                
                # 处理帧数据
                while len(buffer) >= 2:
                    # 检查是否是有效帧开始
                    if buffer[0] == NSA5312Protocol.DEVICE_ADDRESS:
                        frame_length = buffer[1]
                        
                        # 检查帧长度是否合理
                        if frame_length > NSA5312Protocol.MAX_FRAME_SIZE:
                            buffer = buffer[1:]  # 丢弃无效数据
                            continue
                        
                        # 等待完整帧
                        if len(buffer) >= frame_length:
                            frame_data = buffer[:frame_length]
                            buffer = buffer[frame_length:]
                            
                            # 处理帧数据
                            self._process_frame(frame_data)
                        else:
                            # 等待更多数据
                            break
                    else:
                        # 丢弃无效数据
                        buffer = buffer[1:]
                
            except Exception as e:
                self.log(f"读取数据错误: {e}", "ERROR")
                if self.is_running:
                    self._update_state(ConnectionState.ERROR, str(e))
                break
    
    def _write_worker(self):
        """写入线程工作函数"""
        while self.is_running:
            try:
                # 从队列获取待发送数据
                command_data = self.write_queue.get(timeout=0.1)
                
                if not self.serial_port or not self.serial_port.is_open:
                    continue
                
                # 发送完整命令帧（包含地址和数据）
                self._send_command_frame(command_data)
                
            except queue.Empty:
                continue
            except Exception as e:
                self.log(f"发送数据错误: {e}", "ERROR")
                if self.is_running:
                    self._update_state(ConnectionState.ERROR, str(e))
                break
    
    def _send_command_frame(self, frame_data: bytes):
        """发送命令帧（地址+数据）"""
        if not self.serial_port or not self.serial_port.is_open:
            return False
        
        try:
            if len(frame_data) == 0:
                return False
            
            # 第一个字节是地址，使用Mark校验位发送
            address = frame_data[0]
            self.serial_port.parity = serial.PARITY_MARK
            self.serial_port.write(bytes([address]))
            self.serial_port.flush()
            
            # 记录地址发送日志
            self.log(f"发送地址: 0x{address:02X} (Mark校验位)", "DEBUG")
            
            # 短暂延时，确保地址和数据之间的时序
            time.sleep(0.001)  # 1ms延时
            
            # 剩余数据使用Space校验位发送
            if len(frame_data) > 1:
                data_bytes = frame_data[1:]
                self.serial_port.parity = serial.PARITY_SPACE
                self.serial_port.write(data_bytes)
                self.serial_port.flush()
                
                # 记录数据发送日志
                self.log(f"发送数据: {self.protocol.format_hex_data(data_bytes)} (Space校验位)", "DEBUG")
            
            # 记录完整帧日志
            self.log(f"发送完整帧: {self.protocol.format_hex_data(frame_data)}", "DEBUG")
            
            return True
            
        except Exception as e:
            self.log(f"发送命令帧错误: {e}", "ERROR")
            return False
    
    def _process_frame(self, frame_data: bytes):
        """处理接收到的帧数据"""
        try:
            # 解析响应帧
            success, command, status, data = self.protocol.parse_response_frame(frame_data)
            
            # 记录接收日志
            self.log(f"接收数据: {self.protocol.format_hex_data(frame_data)}", "DEBUG")
            
            # 创建响应对象
            response = CommandResponse(
                success=success,
                command=command,
                status=status,
                data=data,
                timestamp=time.time()
            )
            
            if not success:
                response.error_msg = f"帧解析失败，状态码: 0x{status:02X}"
                self.log(response.error_msg, "ERROR")
            
            # 将响应放入队列
            self.response_queue.put(response)
            
            # 如果有数据回调，调用它
            if self.data_callback:
                self.data_callback(frame_data)
            
        except Exception as e:
            self.log(f"处理帧数据错误: {e}", "ERROR")
    
    def send_command(self, command_data: bytes, timeout: float = 3.0) -> CommandResponse:
        """
        发送命令并等待响应
        返回：CommandResponse对象
        """
        if not self.is_running or not self.serial_port:
            return CommandResponse(
                success=False,
                command=0,
                status=StatusCode.OPERATION_FAILURE.value,
                data=b'',
                error_msg="串口未连接"
            )
        
        try:
            # 发送命令
            self.write_queue.put(command_data)
            
            # 等待响应
            start_time = time.time()
            while time.time() - start_time < timeout:
                try:
                    response = self.response_queue.get(timeout=0.1)
                    return response
                except queue.Empty:
                    continue
            
            # 超时
            return CommandResponse(
                success=False,
                command=0,
                status=StatusCode.OPERATION_FAILURE.value,
                data=b'',
                error_msg="命令超时"
            )
            
        except Exception as e:
            return CommandResponse(
                success=False,
                command=0,
                status=StatusCode.OPERATION_FAILURE.value,
                data=b'',
                error_msg=str(e)
            )
    
    def send_command_async(self, command_data: bytes):
        """异步发送命令（不等待响应）"""
        if self.is_running and self.serial_port:
            self.write_queue.put(command_data)
    
    def send_address_only(self, address: int) -> bool:
        """仅发送地址（使用Mark校验位）"""
        if not self.serial_port or not self.serial_port.is_open:
            return False
        
        try:
            # 切换到Mark校验位模式
            original_parity = self.serial_port.parity
            self.serial_port.parity = serial.PARITY_MARK
            self.serial_port.write(bytes([address]))
            self.serial_port.flush()
            
            # 记录发送日志
            self.log(f"发送地址: 0x{address:02X} (Mark校验位)", "DEBUG")
            
            # 恢复原校验位模式
            self.serial_port.parity = original_parity
            return True
            
        except Exception as e:
            self.log(f"发送地址失败: {e}", "ERROR")
            return False
    
    def send_data_only(self, data: bytes) -> bool:
        """仅发送数据（使用Space校验位）"""
        if not self.serial_port or not self.serial_port.is_open:
            return False
        
        try:
            # 确保使用Space校验位
            self.serial_port.parity = serial.PARITY_SPACE
            self.serial_port.write(data)
            self.serial_port.flush()
            
            # 记录发送日志
            self.log(f"发送数据: {self.protocol.format_hex_data(data)} (Space校验位)", "DEBUG")
            return True
            
        except Exception as e:
            self.log(f"发送数据失败: {e}", "ERROR")
            return False
    
    def get_connection_state(self) -> ConnectionState:
        """获取连接状态"""
        with self.state_lock:
            return self.connection_state
    
    def get_last_error(self) -> str:
        """获取最后的错误信息"""
        with self.state_lock:
            return self.last_error
    
    def is_connected(self) -> bool:
        """检查是否已连接"""
        return self.connection_state == ConnectionState.CONNECTED
    
    def flush_input(self):
        """清空输入缓冲区"""
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.reset_input_buffer()
    
    def flush_output(self):
        """清空输出缓冲区"""
        if self.serial_port and self.serial_port.is_open:
            self.serial_port.reset_output_buffer()

class NSA5312CommandHelper:
    """NSA5312命令助手类"""
    
    def __init__(self, serial_handler: SerialHandler):
        self.serial_handler = serial_handler
        self.protocol = NSA5312Protocol()
    
    def get_version(self) -> CommandResponse:
        """获取版本信息"""
        command = self.protocol.build_get_version_command()
        return self.serial_handler.send_command(command)
    
    def read_adc_channel(self, channel: int) -> CommandResponse:
        """读取单个ADC通道"""
        command = self.protocol.build_adc_single_read_command(channel)
        return self.serial_handler.send_command(command)
    
    def control_gpio_row(self, channel: int) -> CommandResponse:
        """控制GPIO行"""
        command = self.protocol.build_gpio_control_command(
            CommandCode.ROW_CONTROL, channel)
        return self.serial_handler.send_command(command)
    
    def control_gpio_col(self, channel: int) -> CommandResponse:
        """控制GPIO列"""
        command = self.protocol.build_gpio_control_command(
            CommandCode.COL_CONTROL, channel)
        return self.serial_handler.send_command(command)
    
    def i2c_write(self, device_addr: int, reg_addr: int, data: bytes) -> CommandResponse:
        """I2C写入"""
        command = self.protocol.build_i2c_command(False, device_addr, reg_addr, data)
        return self.serial_handler.send_command(command)
    
    def i2c_read(self, device_addr: int, reg_addr: int, length: int) -> CommandResponse:
        """I2C读取"""
        command = self.protocol.build_i2c_command(True, device_addr, reg_addr, bytes([length]))
        return self.serial_handler.send_command(command)
    
    def spi_write(self, data: bytes, is_spi4: bool = False) -> CommandResponse:
        """SPI写入"""
        command = self.protocol.build_spi_command(False, data, is_spi4)
        return self.serial_handler.send_command(command)
    
    def spi_read(self, length: int, is_spi4: bool = False) -> CommandResponse:
        """SPI读取"""
        command = self.protocol.build_spi_command(True, bytes([length]), is_spi4)
        return self.serial_handler.send_command(command)
    
    def owi_write(self, data: bytes) -> CommandResponse:
        """OWI写入"""
        command = self.protocol.build_owi_command(False, data)
        return self.serial_handler.send_command(command)
    
    def owi_read(self, length: int) -> CommandResponse:
        """OWI读取"""
        command = self.protocol.build_owi_command(True, bytes([length]))
        return self.serial_handler.send_command(command)
    
    def collect_64ch_data(self, data_type: CommandCode) -> CommandResponse:
        """采集64通道数据"""
        command = self.protocol.build_64ch_collect_command(data_type)
        return self.serial_handler.send_command(command, timeout=5.0)  # 增加超时时间
    
    def send_custom_command(self, command_code: int, parameters: bytes = b'') -> CommandResponse:
        """发送自定义命令"""
        command = self.protocol.build_custom_command(command_code, parameters)
        return self.serial_handler.send_command(command)